// Section.cpp -- Section Class Implementation for decompressing individual
//     sections of Lemmings binary files (*.DAT). Used by Decompress class
// Copyright (C) 2008  Morgan Conbere
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
// USA.
//
// $Id: Section.cpp 18 2008-12-26 20:33:33Z mconbere $
//

#include <stdint.h>

#include <iostream>

#include "Header.h"
#include "Section.h"

namespace decompress
{

Section::Section(uint8_t bitsLeft, uint8_t checkSum, uint16_t decompressedSize, uint16_t compressedSize) :
mHeader(bitsLeft, checkSum, decompressedSize, compressedSize),
mCompressedBuffer(NULL),
mDecompressedBuffer(NULL),
mCheckSum(0),
mDIndex(decompressedSize),
mCIndex(compressedSize - 1),
mCurrent(0)
{
    // Nothing to do here
}

bool Section::decompress(uint8_t* compressedBuffer)
{
    mCompressedBuffer = compressedBuffer;
    
    // Allocated the buffer for the decompressed data
    mDecompressedBuffer = new uint8_t[mHeader.decompressedSize()];
    
    while(mDIndex > 0)
    {
        uint8_t state1 = popBits(1);
        
        if(state1 == 0)
        {
            uint8_t state2 = popBits(1);
            
            if(state2 == 0)
            {
                dumpBytes(popBits(3) + 1);
            }
            else if(state2 == 1)
            {
                copyBytes(2, 8);
            }
        }
        else if(state1 == 1)
        {
            uint8_t state23 = popBits(2);
            
            if(state23 == 0)
            {
                copyBytes(3, 9);
            }
            else if(state23 == 1)
            {
                copyBytes(4, 10);
            }
            else if(state23 == 2)
            {
                copyBytes(popBits(8) + 1, 12);
            }
            else if(state23 == 3)
            {
                dumpBytes(popBits(8) + 9);
            }
        }
    }
    
    return mHeader.checkSum() == mCheckSum;
}

unsigned int Section::popBit()
{
    if(mBitsLeft == 0)
    {
        --mCIndex;
        mCurrent = mCompressedBuffer[mCIndex];
        mBitsLeft = 8;
        mCheckSum ^= mCurrent;
    }
    
    unsigned int result = mCurrent & 1;
    mCurrent >>= 1;
    --mBitsLeft;
    
    return result;
}

unsigned int Section::popBits(unsigned int n)
{
    unsigned int result = 0;
    
    for(unsigned int i = 0; i < n; ++i)
    {
        result <<= 1;
        result |= popBit();
    }
    
    return result;
}

void Section::dumpBytes(unsigned int n)
{
    for(unsigned int i = 0; i < n; ++i)
    {
        --mDIndex;
        mDecompressedBuffer[mDIndex] = popBits(8);
    }
}

void Section::copyBytes(unsigned int n, unsigned int o)
{
    unsigned int offset = popBits(o);
    
    for(unsigned int i = 0; i < n; ++i)
    {
        --mDIndex;
        mDecompressedBuffer[mDIndex] = mDecompressedBuffer[mDIndex + offset + 1];
    }
}

std::ostream& Section::get(std::ostream &out)
{
    return out.write((char *)mDecompressedBuffer, mHeader.decompressedSize());
}

void Section::print(std::ostream &out)
{
    mHeader.print(out);
}
    
} // namespace decompress
